<?php
define('Sgip_CONNECT', 0x00000001); // 请求连接
define('Sgip_CONNECT_RESP', 0x80000001); // 请求连接应答
define('Sgip_TERMINATE', 0x00000002); // 终止连接
define('Sgip_TERMINATE_RESP', 0x80000002); // 终止连接应答
define('Sgip_SUBMIT', 0x00000003); // 提交短信
define('Sgip_SUBMIT_RESP', 0x80000003); // 提交短信应答
define('Sgip_REPORT', 0x5); // 短信状态报告
define('Sgip_REPORT_RESP', 0x80000005); // 短信状态报告应答
class SgipServer
{
    protected $clients;
    protected $serv;
    protected $sequence_id = 1;
    protected $redis_client;
    protected $sp_code = 46042;
    protected $area_code = '0290';

    function run()
    {
        $this->serv = new swoole_server("127.0.0.1", 9503);
        $this->serv->set(array(
            'timeout' => 1, //select and epoll_wait timeout.
            'daemonize' => 1,  //以守护进程执行
            'reactor_num' => 1, //线程数
            'worker_num' => 1, //进程数
            'backlog' => 128, //Listen队列长度
            'max_conn' => 10000,
            'max_request' => 3000,
            'task_max_request'=>3000,
            'dispatch_mode' => 1,
        ));

        $this->serv->addlistener('0.0.0.0', 34325, SWOOLE_SOCK_TCP);
        $this->serv->on('workerstart', array($this, 'onStart'));
        $this->serv->on('connect', array($this, 'onConnect'));
        $this->serv->on('Receive', array($this, 'onReceive'));
        $this->serv->on('close', array($this, 'onClose'));
        $this->serv->start();
    }

    //生成消息头
    //body 消息体
    //cid 命令id
    private function get_header($body,$cid=1,$send_list_id = ''){
        $body_len =strlen($body)+20;
        $CommandId = "3".$this->area_code.$this->sp_code;
        $time_str = date('mdHis',time());
        if($send_list_id){
            $last_code = $send_list_id;
        }else{
            $this->sequence_id = $this->sequence_id + 1;
            $last_code = $this->sequence_id;
        }
        //消息头
        $header = pack('NNNNN',$body_len,$cid,$CommandId*1,$time_str*1,$last_code*1);
        //消息长度
        return $header;
    }

    public function client_fun($data = ''){
        $buffer = $data;
        $socket = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC);
        $socket->on("connect", function(swoole_client $cli) {
            //消息体
            $body = pack('Ca16a16a8',1,46042,'DSADdsdqwewq','');
            //消息头
            $header = $this->get_header($body,1);
            $data = $header.$body;
            $cli->send($data);
        });

        $socket->on("receive", function(swoole_client $cli, $data) use($buffer){
            if($data){
                extract(unpack("Nlength/Ncommand_id/Nfist_number/Ntime_str/Nlast_code", $data));
                $command_id &= 0x0fffffff;
                $sequence_number = $fist_number.$time_str.$last_code;
                $pdu = substr($data, 20);
                switch ($command_id) {
                    case Sgip_CONNECT:
                        $data = $this->Sgip_CONNECT_RESP($pdu, $cli, $buffer);
                        break;
                    case Sgip_SUBMIT:
                       $data = $this->Sgip_SUBMIT_RESP($pdu, $sequence_number,$last_code);
                       break;
                    default:
                        break;
                }
            }
        });
        $socket->on("error", function(swoole_client $cli){
            echo "error\n";
        });
        $socket->on("close", function(swoole_client $cli){
            echo "Connection close\n";
            $this->client_fun();
        });

        $socket->connect('112.90.92.218', 8801);
    }

    /**
     * 获得redis连接
     */
    private function redis_conn()
    {
        if (!$this->redis_client) {
            try {
                $this->redis_client = new Redis();
                if (!$this->redis_client->pconnect('127.0.0.1', '6379')) {
                    return false;
                }
                if (!$this->redis_client->auth('123456')) {
                    return false;
                }

            } catch (\Exception $e) {
                return false;
            }
        }
        return $this->redis_client;
    }

    private function Sgip_CONNECT_RESP($pdu, $cli, $buffer){
        $format = "CStatus/a8Reserve";
        $data = unpack($format, $pdu);
        $status = intval($data['Status']);
        if($status !== 0){
            $cli->close(true);
            unset($cli);
        }
        if($buffer){
            $cli->send($buffer);
        }
        $redis = $this->redis_conn();
        $key = 'active_sgip_9504';
        $redis->set($key, date('Y-m-d H:i:s'), 120);
    }

    private function Sgip_SUBMIT_RESP($pdu, $msg_id, $send_list_id){
        $format = "CResult/a8Reserve";
        $data = unpack($format, $pdu);
        $status = $data['Result'];
        $redis = $this->redis_conn();

        // code_monitor
        $key = 'code_monitor_'.$send_list_id;
        $redis->set($key, 3, 3600*24);

        if($status === 0){
            $key = 'yanzhengma_'.$this->sp_code.'_'.$msg_id;
            $redis->set($key, $send_list_id, 3600*24);
        }else{
            $key = 'yanzhengma_send_result_list';
            $array['send_list_id'] = $send_list_id;
            $array['status'] = 0;
            $array['msg_id'] = '';
            $array['code'] = 'error';
            $array['receive_time'] = date('Y-m-d H:i:s');
            $json = json_encode($array);
            $redis->rpush($key,$json);
        }
    }

    private function Sgip_REPORT_RESP($pdu, $fd){
        $body = pack('Ca8','0', '');
        $header = $this->get_resp_header($body,Sgip_REPORT_RESP, $pdu);//消息头
        $data = $header.$body;
        $res = $this->serv->send($fd, $data);
        return $res;
    }

    private function Sgip_REPORT($pdu, $fd){
        $content_pdu = substr($pdu, 20);

        $result = unpack('Nfist_number/Ntime_str/Nlast_code/CReportType/a21UserNumber/CState/CErrorCode/a8Reserve', $content_pdu);
        $stat = $result['State'];
        $status = 0;
        if($stat === 0){
            $status = 1;
        }
        $error_code = $result['ErrorCode'];
        $msg_id = $result['fist_number'].$result['time_str'].$result['last_code'];

        $redis = $this->redis_conn();
        $key1 = 'yanzhengma_'.$this->sp_code.'_'.$msg_id;
        $send_list_id = $redis->get($key1);

        // code_monitor
        $key = 'code_monitor_'.$send_list_id;
        $redis->set($key, 4, 3600*24);

        if($send_list_id){
            $array['send_list_id'] = $send_list_id;
            $array['status'] = $status;
            $array['msg_id'] = $msg_id;
            $array['code'] = $error_code;
            $array['receive_time'] = date('Y-m-d H:i:s');
            $json = json_encode($array);
            $key = 'yanzhengma_send_result_list';
            $res = $redis->rpush($key,$json);
        }
        $this->Sgip_REPORT_RESP($pdu, $fd);
    }

    private function split_message_unicode($text)
    {
        $max_len = 134;
        $res = array();
        if (mb_strlen($text) <= 140) {
            $res[] = $text;
            return $res;
        }
        $pos = 0;
        $msg_sequence = $this->_message_sequence++;
        $num_messages = ceil(mb_strlen($text) / $max_len);
        $part_no = 1;
        while ($pos < mb_strlen($text)) {
            $ttext = mb_substr($text, $pos, $max_len);
            $pos += mb_strlen($ttext);
            $udh = pack("cccccc", 5, 0, 3, $msg_sequence, $num_messages, $part_no);
            $part_no++;
            $res[] = $udh . $ttext;
        }
        return $res;
    }

    //生成消息头
    //body 消息体
    //cid 命令id
    private function get_resp_header($body,$cid=1,$pdu){
        $body_len =strlen($body)+20;
        $header_1 = pack('NN',$body_len,$cid); //8
        $header_2 = substr($pdu, 8, 12);
        $header = $header_1.$header_2;
        //消息长度
        return $header;
    }

    /*
     * 绑定回复响应
     */
    private function Bind_Resp($pdu, $fd){
        $body = pack('Ca8','0', '');
        $header = $this->get_resp_header($body,Sgip_CONNECT_RESP, $pdu);//消息头
        $data = $header.$body;
        $res = $this->serv->send($fd, $data);
        return $res;
    }

    function onStart($serv)
    {
        $this->serv = $serv;
        echo "Server: start.Swoole version is [" . SWOOLE_VERSION . "]\n";
        $this->client_fun();
    }

    function onConnect($serv, $fd, $from_id)
    {
        echo 'client connect';
        $info = $serv->connection_info($fd, $from_id);
        echo $info['server_port'];
    }

    function onClose($serv, $fd, $from_id)
    {
        echo 'client close';
    }

    function onReceive($serv, $fd, $from_id, $data){
        $info = $serv->connection_info($fd, $from_id);
        if($info['server_port'] == 34325){
            extract(unpack("Nlength/Ncommand_id/Nsequence_number", $data));
                $command_id &= 0x0fffffff;
                switch ($command_id) {
                    case Sgip_CONNECT:
                        $res = $this->Bind_Resp($data,$fd);
                        break;
                    case Sgip_REPORT:
                        $res = $this->Sgip_REPORT($data,$fd);
                        break;
                    default:
                        break;
                }
            return;
        }
        $temp = json_decode($data, true);
        $mobile = $temp['mobile'];
        $content = $temp['content'];
        $send_list_id = $temp['send_list_id']; //发送总表的ID
        if($mobile && $content && $send_list_id){

            // code_monitor
            $redis = $this->redis_conn();
            $key = 'code_monitor_'.$send_list_id;
            $redis->set($key, 2, 3600*24);

            $unicode_text = mb_convert_encoding($content, "UCS-2BE", 'UTF-8'); /* 中文要用UCS-2BE编码，要不收到短信是乱码 */
            $multi_texts = $this->split_message_unicode($unicode_text);
            unset($unicode_text);
            reset($multi_texts);
            list($pos, $part) = each($multi_texts);
            $msg_content = $part;
            $msg_len = strlen($msg_content);//短信长度
            //打包消息体
            $body = pack('a21','10655020048713');//SPNumber
            $body .= pack('a21','000000000000000000000');//ChargeNumber
            $body .= pack('C',1);//UserCount
            $body .= pack('a21','86'.$mobile);//UserNumber
            $body .= pack('a5',$this->sp_code);//CorpId
            $body .= pack('a10','code2');//ServiceType
            $body .= pack('C',1);//FeeType
            $body .= pack('a6','0');//FeeValue
            $body .= pack('a6','0');//GivenValue
            $body .= pack('C',0);//AgentFlag
            $body .= pack('C',2);//MorelatetoMTFlag
            $body .= pack('C',9);//Priority
            $body .= pack('a16','');//ExpireTime
            $body .= pack('a16','');//ScheduleTime
            $body .= pack('C',1);//ReportFlag
            $body .= pack('C',0);//TP_pid
            $body .= pack('C',0);//TP_udhi
            $body .= pack('C',8);//MessageCoding
            $body .= pack('C',0);//MessageType
            $body .= pack('N',$msg_len);//MessageLength
            $body .= pack("a$msg_len",$msg_content);//MessageContent
            $body .= pack('a8','');//MessageContent
            $header = $this->get_header($body,Sgip_SUBMIT,$send_list_id);//消息头
            $data = $header.$body;
            $this->client_fun($data);
        }
    }
}

$serv = new SgipServer();
$serv->run();
